﻿using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.Threading;
using System.IdentityModel.Claims;

namespace WcfContrib.Handlers
{
    /// <summary>
    /// A helper class for dealing with identity context
    /// </summary>
    public static class IdentityHelper
    {
        const IdentityScope DefaultScope = IdentityScope.ClaimSet;
        const bool DefaultStripDomain = true;

        /// <summary>
        /// Gets the name of the user from the claim identity scope.
        /// Strips domain if exists.
        /// </summary>
        /// <returns></returns>
        public static string GetUsername()
        {
            return GetUsername(DefaultScope);
        }

        /// <summary>
        /// Gets the name of the user from the given identity scope.
        /// Strips domain if exists.
        /// </summary>
        /// <param name="scope">The identity scope.</param>
        /// <returns></returns>
        public static string GetUsername(IdentityScope scope)
        {
            return GetUsernameInternal(DefaultStripDomain, scope);
        }

        /// <summary>
        /// Gets the name of the user from the claim identity scope.
        /// </summary>
        /// <param name="stripDomain">if set to <c>true</c>, the domain is stripped.</param>
        /// <returns></returns>
        public static string GetUsername(bool stripDomain)
        {
            return GetUsername(stripDomain, DefaultScope);
        }

        /// <summary>
        /// Gets the name of the user from the claim identity scope.
        /// </summary>
        /// <param name="stripDomain">if set to <c>true</c>, the domain is stripped.</param>
        /// <param name="scope">The identity scope.</param>
        /// <returns></returns>
        public static string GetUsername(bool stripDomain, IdentityScope scope)
        {
            return GetUsernameInternal(stripDomain, scope);
        }

        /// <summary>
        /// Gets the name of the user from the identity scope.
        /// </summary>
        /// <param name="stripDomain">if set to <c>true</c>, the domain is stripped.</param>
        /// <param name="scope">The identity scope.</param>
        /// <param name="context">The security context.</param>
        /// <returns></returns>
        public static string GetUsername(bool stripDomain, IdentityScope scope, ServiceSecurityContext context)
        {
            return GetUsernameInternal(stripDomain, scope, context);
        }

        private static string GetUsernameInternal(bool stripDomain, IdentityScope scope)
        {
            return GetUsernameInternal(stripDomain, scope, null);
        }

        private static string GetUsernameInternal(bool stripDomain, IdentityScope scope, ServiceSecurityContext context)
        {
            string ret = string.Empty;

            if (context == null)
            {
                try
                {
                    context = ServiceSecurityContext.Current;
                }
                catch (ObjectDisposedException) { }
            }
            
            bool scopeIsNull = (context == null);

            if (scope == IdentityScope.Windows)
            {
                if (!scopeIsNull)
                {
                    if (context.WindowsIdentity != null)
                    {
                        ret = context.WindowsIdentity.Name;
                    }
                }
            }
            else if (scope == IdentityScope.Primary)
            {
                if (!scopeIsNull)
                {
                    ret = context.PrimaryIdentity.Name;
                }
            }
            else if (scope == IdentityScope.ThreadPrincipal)
            {
                if (Thread.CurrentPrincipal != null && Thread.CurrentPrincipal.Identity != null)
                {
                    ret = Thread.CurrentPrincipal.Identity.Name;
                }
            }
            else if (scope == IdentityScope.ClaimSet)
            {
                if (!scopeIsNull)
                {
                    bool searchForPossess = true;
                    foreach (ClaimSet claimSet in context.AuthorizationContext.ClaimSets)
                    {
                        foreach (Claim claim in claimSet)
                        {
                            if (claim.ClaimType == ClaimTypes.Name && (claim.Right == Rights.Identity || claim.Right == Rights.PossessProperty))
                            {
                                if (claim.Right == Rights.Identity)
                                {
                                    return (string)claim.Resource;
                                }
                                else if (searchForPossess && claim.Right == Rights.PossessProperty)
                                {
                                    searchForPossess = false;

                                    ret = (string)claim.Resource;
                                }

                            }
                        }
                    }
                }
            }

            if (!string.IsNullOrEmpty(ret))
            {
                if (stripDomain)
                {
                    ret = StripDomain(ret);
                }

                ret = ret.ToLower();
            }

            return ret ?? string.Empty;
        }

        private static string StripDomain(string input)
        {
            int idx = input.LastIndexOf("\\");
            if (idx >= 0)
            {
                input = input.Substring(idx + 1);
            }

            return input;
        }
    }
}
